// 日志格式化模块:
// 作用: 对日志消息进行格式化，组织称为指定格式的字符串
// %d 日期
// %T 缩进
// %t 线程 id
// %p 日志级别
// %c 日志器名称
// %f 文件名
// %l 行号
// %m 日志消息
// %n 换行
// 格式化字符串控制了日志的输出格式。定义格式化字符，是为了让日志系统进行日志格式化更加的灵活方便。
// 示例格式化字符串: [%d{%H:%M:%S}][%T%t][%c][%p][%f:%l]%T%m%n
// 假设有日志消息 LogMsg 包含以下信息:
// size_t _ctime = 1111111
// LogLevel::value _level = DEBUG
// size_t _line = 53
// std::thread::id _tid = 12345678
// std::string _file = main.c
// std::string _logger = root
// std::string _payload = "套接字创建失败.."
// 最终格式化输出: [18:23:54][12345678][root][DEBUG][main.c:53] 套接字创建失败..\n
// 成员:
// 格式化字符串：由用户来定义想要输出的格式
// 格式化子项数组：对格式化字符串进行解析，保存了日志消息要素的排序。不同的格式化子项，会从日志消息中取出指定的元素，转换为字符串，然后组织成字符串的顺序。示例格式化子项数组: [%d{%H:%M:%S}][%T%t][%c][%p][%f:%l]%T%m%n
// 格式化子项:
// 1、其他信息（非格式化字符）子项： [
// 2、日期子项：%H:%M:%S
// 3、其他信息子项
// 4、其他信息子项： ]
// 5、文件名子项
// 6、其他信息子项： :
// 7、其他信息子项
// 8、消息主体子项
// 8、换行子项
// 需要先完成的：格式化子项类的定义与实现
// 作用：从日志消息中取出指定的元素，追加到一块内存空间中
//设计思想:
//抽象一个格式化子项基类
//基于基类，派生出不同的格式化子项类：
//主体消息，日志等级，时间子项，文件名，行号，日志器名称，线程 ID，制表符，换行，其他。
//这样就可以在父类中定义父类指针的数组，指向不同的格式化子项类对象
#ifndef _M_FORMAT_H_
#define _M_FORMAT_H_
#include <ctime>
#include "level.hpp"
#include "message.hpp"
#include "util.hpp"
#include <memory>
#include<vector>
#include<cassert>
namespace logsys {
    // 抽象格式化子项基类，定义了格式化操作的接口规范
    class FormatItem {
    public:
        using ptr = std::shared_ptr<FormatItem>;
        // 纯虚函数，用于将日志消息中的特定元素格式输出到输出流中，具体实现由派生类完成
        virtual void format(std::ostream &out, LogMsg &msg) = 0; 
    };

    // 派生格式化子项类 - 消息格式化项
    class MsgFormatItem : public FormatItem {
    public:
        // 重写基类的format函数，将日志消息的有效消息数据输出到输出流
        void format(std::ostream &out, LogMsg &msg) override { 
            out << msg._payload;
        }
    };

    // 派生格式化子项类 - 日志等级格式化项
    class LevelFormatItem : public FormatItem {
    public:
        // 重写基类的format函数，将日志消息的日志等级转换为字符串后输出到输出流
        void format(std::ostream &out, LogMsg &msg) override { 
            out << LogLevel::toString(msg._level);
        }
    };

    // 派生格式化子项类 - 时间格式化项
    class TimeFormatItem : public FormatItem {
    public:
        // 构造函数，接受一个时间格式字符串参数，用于指定时间的输出格式，默认格式为"%H:%M:%S"
        TimeFormatItem(const std::string &fmt = "%H:%M:%S") : _time_fmt(fmt) {} 
        // 重写基类的format函数，将日志消息的时间戳转换为指定格式的时间字符串并输出到输出流
        void format(std::ostream &out, LogMsg &msg) override { 
            struct tm t;
            localtime_r(&msg._ctime, &t); // 将时间戳转换为tm结构体表示的本地时间
            char tmp[32] = {0};
            strftime(tmp, 31, _time_fmt.c_str(), &t); // 按照指定格式将时间格式化到字符数组tmp中
            out << tmp;
        }
    private:
        std::string _time_fmt; // 存储时间格式字符串
    };

    // 派生格式化子项类 - 文件名格式化项
    class FileFormatItem : public FormatItem {
    public:
        // 重写基类的format函数，将日志消息的源码文件名输出到输出流
        void format(std::ostream &out, LogMsg &msg) override { 
            out << msg._file;
        }
    };

    // 派生格式化子项类 - 行号格式化项
    class LineFormatItem : public FormatItem {
    public:
        // 重写基类的format函数，将日志消息的源代码行号输出到输出流
        void format(std::ostream &out, LogMsg &msg) override { 
            out << msg._line;
        }
    };

    // 派生格式化子项类 - 线程ID格式化项
    class ThreadFormatItem : public FormatItem {
    public:
        // 重写基类的format函数，将日志消息的线程ID输出到输出流
        void format(std::ostream &out, LogMsg &msg) override { 
            out << msg._tid;
        }
    };

    // 派生格式化子项类 - 日志器名格式化项
    class LoggerFormatItem : public FormatItem {
    public:
        // 重写基类的format函数，将日志消息的日志器名称输出到输出流
        void format(std::ostream &out, LogMsg &msg) override { 
            out << msg._logger;
        }
    };

    // 派生格式化子项类 - 制表符格式化项
    class TabFormatItem : public FormatItem {
    public:
        // 重写基类的format函数，向输出流输出制表符\t
        void format(std::ostream &out, LogMsg &msg) override { 
            out << "\t";
        }
    };

    // 派生格式化子项类 - 换行格式化项
    class NLineFormatItem : public FormatItem {
    public:
        // 重写基类的format函数，向输出流输出换行符\n
        void format(std::ostream &out, LogMsg &msg) override { 
            out << "\n";
        }
    };

    // 派生格式化子项类 - 其他内容格式化项
    class OtherFormatItem : public FormatItem {
    public:
        // 构造函数，接受一个字符串参数，用于存储要输出的其他内容
        OtherFormatItem(const std::string &str) : _str(str) {} 
        // 重写基类的format函数，将存储的其他内容字符串输出到输出流
        void format(std::ostream &out, LogMsg &msg) override { 
            out << _str;
        }
    private:
        std::string _str; // 存储其他内容字符串
    };

    /*
 * %d 表示日期 ，包含子格式 {%H:%M:%S}
 * %t 表示线程ID
 * %c 表示日志器名称
 * %f 表示源码文件名
 * %l 表示源码行号
 * %p 表示日志级别
 * %T 表示制表符缩进
 * %m 表示主体消息
 * %n 表示换行
 */
class Formatter {
public:
    using ptr = std::shared_ptr<Formatter>;
    // 构造函数，接受一个格式化模式字符串作为参数，默认值为"[%d{%H:%M:%S}][%t][%c][%f:%l][%p]%T%m%n"
    // 该函数会解析传入的格式化模式字符串，并初始化相关成员变量
    Formatter(const std::string &pattern = "[%d{%H:%M:%S}][%t][%c][%f:%l][%p]%T%m%n") :
        _pattern(pattern) {
        assert(parsePattern());//必须解析成功
    }
    // 对日志消息进行格式化，并输出到指定的输出流中
    void format(std::ostream &out, LogMsg &msg) const {
        for (auto &item : _items) {
            item->format(out, msg);
        }
    }
    // 对日志消息进行格式化，返回格式化后的字符串
    std::string format(LogMsg &msg) {
        std::stringstream ss;
        format(ss, msg);
        return ss.str();
    }
private:
// 解析格式化模式字符串，将其拆分为不同的格式化子项，并创建对应的格式化子项对象
// abcde[%d{%H:%M:%S}][%p]%T%m%n
// 没有以 % 起始的字符串都是原始字符串
// 处理思想：不是 %，则一直向后走，直到遇到 %，则是原始字符串的结束
//          遇到 %，看看紧随其后的这个字符是不是 %，如果是就是 %% 字符
//          如果不是，则代表紧随其后的这个字符是个格式化字符
//          紧随格式化字符之后，有没有 {，如果有，则 {之后，} 之前的数据是格式化字符的子格式
//          规则字符串的处理过程是一个循环的过程:
// while () {
// 处理原始字符串
// 原始字符串结束后，遇到 %，则处理一个格式化字符
// }
// 在处理过程中，需要将处理得到的信息保存下来
// key=nullptr, val=abcde[
// key=d, val = %H:%M:%S
// key=nullptr, val = ][
// key=p, val=nullptr
// key=nullptr, val= ]
// key=T, val=null
// key=m, val=null
// key=n, val=null
// 根据这个创建格式化子项数组
// 得到了数组后，根据数组内容，创建对应的格式化子项对象，添加到 items 成员数组中
    bool parsePattern() {
        // 1. 对格式化规则字符串进行解析
        // 定义一个字符串向量，用于存储解析后的格式化子项
        std::vector<std::pair<std::string, std::string>> fmt_order;
        size_t pos = 0;
        std::string key, val;
        // 循环解析格式化模式字符串
        while (pos < _pattern.size()) {
            // 1/. 处理原始字符串 -- 判断是否是%，不是就是原始字符
            if (_pattern[pos] != '%') {
                val.push_back(_pattern[pos++]);
                continue;
            }
            // 能走下来就代表pos位置就是%字符，%%处理为一个原始%字符
            if (pos + 1 < _pattern.size() && _pattern[pos + 1] == '%') {
                val.push_back('%');
                pos += 2;
                continue;
            }
            // 能走下来，代表%后边是个格式化字符，代表原始字符串处理完毕
            if (!val.empty()) {
                fmt_order.push_back(std::make_pair("", val));
                val.clear();
            }
            // 这时候pos指向的是%位置，是格式化字符的处理
            pos += 1; // 这一步之后，pos指向格式化字符位置
            if (pos == _pattern.size()) {
                std::cout << "之后，没有对应的格式化字符!\n";
                return false;
            }
            key = _pattern[pos];
            pos += 1; // 这时候pos指向格式化字符后的位置
            if (pos < _pattern.size() && _pattern[pos] == '{') {
                pos += 1; // 这时候pos指向格式子规则的起始位置
                while (pos < _pattern.size() && _pattern[pos] != '}') {
                    val.push_back(_pattern[pos++]);
                }
                // 走到末尾跳出了循环，则代表没有遇到'}，代表格式是错误的
                if (pos == _pattern.size()) {
                    std::cout << "子规则{}匹配出错!\n";
                    return false;//每找到}
                }
                pos += 1; // 因为这时候pos指向的是')'位置，向后走一步，走到下次处理的新位置
            }
            fmt_order.push_back(std::make_pair(key, val));
            key.clear();
            val.clear();
        }
        // 2. 根据解析得到的数据初始化格式化子项数组成员
        for (auto &it : fmt_order) {
            _items.push_back(createItem(it.first, it.second));
        }
        return true;
    }
    // 根据不同的格式化字符创建不同的格式化子项对象
    FormatItem::ptr createItem(const std::string &key, const std::string &val) {
        /*
         * %d 表示日期 ，包含子格式 {%H:%M:%S}
         * %t 表示线程ID
         * %c 表示日志器名称
         * %f 表示源码文件名
         * %l 表示源码行号
         * %p 表示日志级别
         * %T 表示制表符缩进
         * %m 表示主体消息
         * %n 表示换行
         */
        if (key == "d") return std::make_shared<TimeFormatItem>(val);
        if (key == "t") return std::make_shared<ThreadFormatItem>();
        if (key == "c") return std::make_shared<LoggerFormatItem>();
        if (key == "f") return std::make_shared<FileFormatItem>();
        if (key == "l") return std::make_shared<LineFormatItem>();
        if (key == "p") return std::make_shared<LevelFormatItem>();
        if (key == "T") return std::make_shared<TabFormatItem>();
        if (key == "m") return std::make_shared<MsgFormatItem>();
        if (key == "n") return std::make_shared<NLineFormatItem>();
        if (key == "")  return  std::make_shared<OtherFormatItem>(val);
        std::cout << "没有对应的格式化字符:%" << key << std::endl;
        abort();
        return FormatItem::ptr();
    }
private:
    std::string _pattern; // 格式化规则字符串
    std::vector<FormatItem::ptr> _items; // 存储格式化子项对象的向量
};

}

#endif